package net.cyndeline.rldungeon.dgs.graph

import net.cyndeline.rldungeon.dgs.objects.{LevelObject, Activator}

/**
 * Extended by vertex-classes, representing rooms in the map graph.
 *
 * @param id a unique id for this room that is shared only by copies derived when modifying the room.
 * @param distanceToGoal The number of edges that must be traversed to reach the level goal from this room (does not
 *                       take corridor/room content such as locked doors into consideration). Can be set to any value
 *                       by default by the implementing class, as it will be modified by the level strategy once
 *                       the map topology is finished.
 * @param pointlessAreaData Should be set to None by default. If the room represented by this object is deemed to be
 *                    pointless by the level generator (i.e the goal of the level can be reached without passing this
 *                    room), then this option will be set to contain all data related to the area of pointless rooms
 *                    this room belongs to. Note that this data isn't removed by any algorithm dealing with pointless
 *                    areas, to allow productions to filter rooms based on whether they used to be pointless or not.
 *                    If you want to check if a room is currently pointless, isPointless() should be used instead.
 * @param pointlessStatus True if this room is pointless, otherwise false. Should be set to false by default.
 */
// TODO Write in documentation that equals and hashcode are needed to ensure the same result from the level algorithm, since they affect the node ordering in the graph
abstract class Room[RoomType <: Room[RoomType]](val id: Int,
                                                val distanceToGoal: Int,
                                                val pointlessAreaData: Option[PointlessAreaData[RoomType]],
                                                pointlessStatus: Boolean) {

  def this(id: Int, distance: Int) = this(id, distance, None, false)

  def this(id: Int, distance: Int, pointlessAreaData: Option[PointlessAreaData[RoomType]]) = this(id, distance, pointlessAreaData, false)

  final def isPointless: Boolean = pointlessStatus

  /**
   * Produces a copy of this room with a specified distance to the goal by setting the distanceToGoal parameter in
   * this class.
   *
   * @param d Distance to the goal from this room.
   * @return A copy of this room with the specified goal distance.
   */
  def withDistanceToGoal(d: Int): RoomType

  /**
   * Produces a copy of this room with a set pointless area data object.
   * @param data Pointless area data.
   * @return A copy of this room with the specified pointless area data.
   */
  def withPointlessData(data: PointlessAreaData[RoomType]): RoomType

  /**
   * Produces a copy of this room with its pointless status set by setting the pointlessStatus parameter in
   * this class.
   *
   * @param pointless True if this room is deemed pointless, otherwise false.
   * @return A copy of this room with its pointless status set.
   */
  def withPointlessStatus(pointless: Boolean): RoomType

  /**
   * Checks if the room can hold additional rewards. Used when dealing with pointless areas.
   * @return True if the room can hold additional rewards, otherwise false.
   */
  def canHoldAdditionalRewards: Boolean

  /**
   * Checks if the room can hold additional activators. Used when dealing with pointless areas.
   * @return True if the room can hold additional activators, otherwise false.
   */
  def canHoldAdditionalActivators: Boolean

  /**
   * Checks if this room is the start of a level.
   * @return True if the room is the starting point of the level, otherwise false.
   */
  def isStart: Boolean

  /**
   * Checks if this room is the goal of a level.
   * @return True if the room is the goal point of the level, otherwise false.
   */
  def isGoal: Boolean

  /**
   * @param other Room to compare this room to.
   * @return True if both rooms contain the same data (even if they're different object instances), otherwise false.
   */
  //TODO Write an object that creates two new rooms from a user-sent room object and checks that id remains the same and that the object instance id's are different between all three rooms. Also test hashcode this way so that it doesn't depend on instance address.
  override def equals(other: Any): Boolean

  /**
   * @return An integer hash code based on the same data as equals.
   */
  override def hashCode: Int

}
